[AWS CDK] Lambda@Edge関数の作成または更新時に自動的に最新バージョンを発行し、CloudFront Distributionに紐付ける
こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、AWS CDKで、Lambda@Edge関数の作成または更新時に自動的に関数の最新バージョンの発行とCloudFront Distributionへの紐付けを行う方法を確認してみました。
Lambda@Edge関数の作成または更新時の手順
CloudFront Distributionに紐付けているLambda@Edge関数を作成または更新する際は、次の手順を踏む必要があります。
- Lambda@Edge関数の
$LATEST
バージョンを作成または更新する。 - 関数の最新バージョンを発行する。
- 発行した関数の最新バージョンをCloudFront Distributionに紐付けする。
Lambda@Edge関数およびCloudFront DistributionをAWS CDKで構築している場合に、上記の手順を行う方法を確認してみます。
環境
- typescript@3.9.10
- aws-cdk@1.145.0
やってみた
スタックの定義は次のようになります。ポイントとしては、Lambda@Edge関数との紐付け設定(edgeLambdas
)でcurrentVersion
により関数バージョンを指定することです。
import * as cdk from '@aws-cdk/core'; import * as cloudfront from '@aws-cdk/aws-cloudfront'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; import * as s3deploy from '@aws-cdk/aws-s3-deployment'; import * as cloudfront_origins from '@aws-cdk/aws-cloudfront-origins'; import * as path from 'path'; import * as lambda from '@aws-cdk/aws-lambda'; export class AwsCdkAppStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props: cdk.StackProps) { super(scope, id, props); const websiteBucket = new s3.Bucket(this, 'WebsiteBucket', { removalPolicy: cdk.RemovalPolicy.DESTROY, }); const originAccessIdentity = new cloudfront.OriginAccessIdentity( this, 'OriginAccessIdentity', { comment: 'website-distribution-originAccessIdentity', } ); const webSiteBucketPolicyStatement = new iam.PolicyStatement({ actions: ['s3:GetObject'], effect: iam.Effect.ALLOW, principals: [ new iam.CanonicalUserPrincipal( originAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId ), ], resources: [`${websiteBucket.bucketArn}/*`], }); websiteBucket.addToResourcePolicy(webSiteBucketPolicyStatement); //Lambda@Edge関数 const websiteAddHeaderFunction = new cloudfront.experimental.EdgeFunction( this, 'WebsiteAddHeaderFunction', { code: lambda.Code.fromAsset( path.join(__dirname, '../src/lambda/website-add-header') ), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_14_X, } ); const distribution = new cloudfront.Distribution(this, 'Distribution', { comment: 'website-distribution', defaultRootObject: 'index.html', errorResponses: [ { ttl: cdk.Duration.seconds(300), httpStatus: 403, responseHttpStatus: 403, responsePagePath: '/error.html', }, { ttl: cdk.Duration.seconds(300), httpStatus: 404, responseHttpStatus: 404, responsePagePath: '/error.html', }, ], defaultBehavior: { allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD, cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD, cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED, viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, origin: new cloudfront_origins.S3Origin(websiteBucket, { originAccessIdentity, }), //Lambda@Edge関数との紐付け設定 edgeLambdas: [ { eventType: cloudfront.LambdaEdgeEventType.ORIGIN_RESPONSE, functionVersion: websiteAddHeaderFunction.currentVersion, }, ], }, priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL, }); new s3deploy.BucketDeployment(this, 'WebsiteDeploy', { sources: [ s3deploy.Source.data( '/index.html', `<html><body><h1>Hello World</h1></body></html>` ), s3deploy.Source.data( '/error.html', `<html><body><h1>Error!!!!!!!!!!!!!</h1></body></html>` ), s3deploy.Source.data('/favicon.ico', ''), ], destinationBucket: websiteBucket, distribution: distribution, distributionPaths: ['/*'], }); } }
動作
初回作成時
まずは初回作成時の動作を見てみます。
CDKデプロイを行い、Lambda@Edge関数およびDistributionへの紐付けを作成します。
作成後に、関数のバージョン一覧を確認すると、$LATEST
と1
が発行されています。
$ aws lambda list-versions-by-function \ --region us-east-1 \ --function-name ${functionName} { "Versions": [ { "FunctionName": "websiteAddHeaderFunction", "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:$LATEST", "Runtime": "nodejs14.x", "Role": "arn:aws:iam::XXXXXXXXXXXX:role/AwsCdkAppStack2-websiteAddHeaderFunctionFnServiceR-1LJSCURJNT1ZD", "Handler": "index.handler", "CodeSize": 308, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2022-02-27T15:41:58.463+0000", "CodeSha256": "8Ux3E5BZPzZ8zf53zCb5sJ5R57r5iKCT/GXhW7k6qSA=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "8563719c-ea6a-4460-a150-20be01c2dc2a", "PackageType": "Zip", "Architectures": [ "x86_64" ] }, { "FunctionName": "websiteAddHeaderFunction", "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:1", "Runtime": "nodejs14.x", "Role": "arn:aws:iam::XXXXXXXXXXXX:role/AwsCdkAppStack2-websiteAddHeaderFunctionFnServiceR-1LJSCURJNT1ZD", "Handler": "index.handler", "CodeSize": 308, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2022-02-27T15:42:19.808+0000", "CodeSha256": "8Ux3E5BZPzZ8zf53zCb5sJ5R57r5iKCT/GXhW7k6qSA=", "Version": "1", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "0c280f9f-5d63-464a-b3c2-93d2370147c2", "PackageType": "Zip", "Architectures": [ "x86_64" ] } ] }
またDistributionの関数紐付け設定を見ると、関数のバージョン1
が設定されています。
$ aws cloudfront get-distribution \ --region us-east-1 \ --id ${distributionId} \ --query 'Distribution.DistributionConfig.DefaultCacheBehavior.LambdaFunctionAssociations' { "Quantity": 1, "Items": [ { "LambdaFunctionARN": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:1", "EventType": "viewer-response", "IncludeBody": false } ] }
関数更新時
Lambda@Edge関数を何かしら更新した際の動きも見てみます。関数のソースコードに変更を加え、CDKデプロイします。
デプロイ後の関数のバージョン一覧を確認すると、新しく2
が発行されています。
$ aws lambda list-versions-by-function \ --region us-east-1 \ --function-name ${functionName} \ --query 'Versions[2]' { "FunctionName": "websiteAddHeaderFunction", "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:2", "Runtime": "nodejs14.x", "Role": "arn:aws:iam::XXXXXXXXXXXX:role/AwsCdkAppStack2-websiteAddHeaderFunctionFnServiceR-1LJSCURJNT1ZD", "Handler": "index.handler", "CodeSize": 321, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2022-02-27T15:56:28.725+0000", "CodeSha256": "1J6HzcVop9XOdXWhJgUIdjaz585LK6BDhhcUPPYNuRE=", "Version": "2", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "e6dc5927-155a-49e7-8dc6-2d70d812722a", "PackageType": "Zip", "Architectures": [ "x86_64" ] }
またDistributionの関数紐付け設定を見ると、関数のバージョン2
が設定されています。
$ aws cloudfront get-distribution \ --region us-east-1 \ --id ${distributionId} \ --query 'Distribution.DistributionConfig.DefaultCacheBehavior.LambdaFunctionAssociations' { "Quantity": 1, "Items": [ { "LambdaFunctionARN": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:2", "EventType": "viewer-response", "IncludeBody": false } ] }
また、関数の設定(タイムアウト値など)を更新した場合でも、同様の動作となることを確認できました。
その他
$LATESTバージョンは紐付けできない
関数の$LATEST
バージョンはDistributionに紐付けできないことを確認してみます。
$LATEST
バージョンの指定はlatestVersion
を使用します。
edgeLambdas: [ { eventType: cloudfront.LambdaEdgeEventType.ORIGIN_RESPONSE, functionVersion: websiteAddHeaderFunction.latestVersion, }, ],
変更をCDKデプロイすると、$LATEST
は設定できない旨のエラーとなりました。想定通りの動作です。
$ cdk deploy /Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts:101 throw new Error('$LATEST function version cannot be used for Lambda@Edge');
現在はaddVersionメソッドはDeprecated
aws-lambda/Function
クラスでは、今まで関数の新しいバージョンを発行する際にはaddVersion
を使用してきたかと思いますが、現在はDeprecatedとなっています。そして代わりにcurrentVersion
を使用してバージョンを取得するようにとあります。
⚠️ Deprecated: This method will create an AWS::Lambda::Version resource which snapshots the AWS Lambda function at the time of its creation and it won't get updated when the function changes. Instead, use this.currentVersion to obtain a reference to a version resource that gets automatically recreated when the function configuration (or code) changes.
一方で今回Lambda@Edge関数の作成に使用したaws-cloudfront-origins/experimental/EdgeFunction
では、addVersion
は使えません。currentVersion
などでバージョンを指定する必要があります。
参考
- list-versions-by-function — AWS CLI 2.4.21 Command Reference
- get-distribution — AWS CLI 2.4.21 Command Reference
以上